﻿// 游戏难度
const GameDifficulty = {
    EASY: 9,    // 简单
    MEDIUM: 25, // 中等
    HARD: 36    // 困难
}

// 游戏状态
const GameStatus = {
    END: 1,     // 已结束
    PLAYING: 0  // 游戏中
}

/**
 * 游戏类构造方法。
 * @param drawer            绘图者
 * @param gameBoard         游戏区域
 * @param gameBoardCanvas   游戏区域画布
 */
function Game(drawer, gameBoard, gameBoardCanvas) {
    var mDifficulty = GameDifficulty.EASY                       // 难度
    const mDrawer = drawer                                      // 绘图者
    var mEmptyPoint = null                                      // 空点，即没有图的那个点
    const mGameBoard = gameBoard                                // 游戏区域
    const mGameBoardCanvas = gameBoardCanvas                    // 游戏区域画布
    const mImage = document.createElement("canvas")             // 图片
    const mGameOverEvent = document.createEvent("customEvent")   // 游戏结束事件
    var mPicHasBorder = true                                    // 图片控件是否有边框标志
    var mPictures = null                                        // 图片控件数组
    var mStatus = GameStatus.END                                // 状态

    // 初始化成员
    mGameOverEvent.initEvent(Const.UM_GAME_OVER, false, false)
    mImage.width = Const.GAME_BOARD_WIDTH
    mImage.height = Const.GAME_BOARD_HEIGHT

    /**
     * 创建所有图片控件。
     */
    function createPictures() {
        var left, top                                       // 图片控件位置
        const baseCount = Math.sqrt(mDifficulty)            // 一行（列）的图片控件数
        const size = Const.EMPTY_AREA_WIDTH / baseCount     // 图片控件尺寸
        const rect = new Rect()                             // 图片控件位置、尺寸
        var j = 0                                           // 换行标志，0为第一行，1为第二行，依次类推

        // 删除所有图片控件
        deletePictures()

        // 创建所有图片控件
        mPictures = new Array(Const.MAX_PICTURE_CTRL_COUNT)
        for (var i = 0; i < mDifficulty; i++) {
            if (i % baseCount == 0 && i != 0) { // 如果一行满了，换到下一列
                j++
            }

            // 计算左上角坐标
            left = Const.EMPTY_AREA_X + size * (i % baseCount)
            top = Const.EMPTY_AREA_Y + size * j

            // 设置位置、尺寸
            rect.set(left, top, left + size, top + size)

            // 创建图片控件
            mPictures[i] = new PictureCtrl(mPicHasBorder, mImage, mGameBoard, rect)
            mPictures[i].onclick = movePicture
        }
    }

    /**
     * 删除所有图片控件。
     */
    function deletePictures() {
        if (mPictures == null) {
            return
        }

        for (var i = 0; i < mPictures.length; i++) {
            if (mPictures[i] == null) {
                break
            }
            mGameBoard.removeChild(mPictures[i])
        }
    }

    /**
     * 游戏结束。
     */
    function gameOver() {
        // 将状态设置为已结束
        mStatus = GameStatus.END

        // 发送通知
        document.dispatchEvent(mGameOverEvent)
    }

    /**
     * 获取游戏难度。
     * @return 游戏难度
     */
    this.getDifficulty = function() {
        return mDifficulty
    }

    /**
     * 获取游戏状态。
     * @return 游戏状态
     */
    this.getStatus = function() {
        return mStatus
    }

    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    function isWon() {
        var result = false
        for (var i = 0; i < mDifficulty; i++) {
            result = mPictures[i].isOnOriginalPosition()
            if (!result) {
                break
            }
        }
        return result
    }

    /**
     * 移动图片控件。
     * @param e 事件相关信息
     */
    function movePicture(e) {
        if (mStatus != GameStatus.PLAYING) {
            return
        }

        const pictureCtrl = e.srcElement
        const temp = pictureCtrl.getPosition()
        const position = new Point(temp.x, temp.y)

        // 如果要移动的图片控件与空点相邻，则移动
        if (position.y == mEmptyPoint.y && Math.abs(position.x - mEmptyPoint.x) == pictureCtrl.getSize() ||
            position.x == mEmptyPoint.x && Math.abs(position.y - mEmptyPoint.y) == pictureCtrl.getSize()) {
            pictureCtrl.setPosition(mEmptyPoint)
            mEmptyPoint = position

            // 判断是否胜利
            if (isWon()) {
                gameOver()
            }
        }
    }

    /**
     * 获取图片控件是否有边框。
     * @return 有边框则为 true，否则为 false
     */
    this.pictureHasBorder = function() {
        return mPicHasBorder
    }

    /**
     * 设置游戏难度。
     * @param diff  游戏难度
     */
    this.setDifficulty = function(diff) {
        // 设置难度
        mDifficulty = diff

        // 重新创建所有图片
        createPictures()

        // 将状态设置为已结束
        mStatus = GameStatus.END
    }

    /**
     * 设置图片。
     * @param filePath  图片文件路径
     */
    this.setPicture = function(filePath) {
        // 加载图片
        const image = new Image()
        image.onload = function() {
            // 将图片画到游戏区域上
            mImage.getContext("2d").drawImage(image, 0, 0, image.width, image.height, 0, 0, mImage.width, mImage.height)
            mDrawer.drawImage(mImage, 0, 0, Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT)

            // 画中间的空白区域
            mDrawer.fillStyle = Const.EMPTY_AREA_COLOR
            mDrawer.fillRect(Const.EMPTY_AREA_X, Const.EMPTY_AREA_Y, Const.EMPTY_AREA_WIDTH, Const.EMPTY_AREA_HEIGHT)
            
            // 通知游戏区域重绘
            mGameBoardCanvas.repaint()

            // 创建所有图片
            createPictures()
        }
        image.src = filePath

        // 将状态设置为已结束
        mStatus = GameStatus.END
    }

    /**
     * 设置图片控件是否有边框。
     * @param hasBorder 有边框则为 true，否则为 false
     */
    this.setPictureBorder = function(hasBorder) {
        mPicHasBorder = hasBorder

        // 设置所有图片控件的边框
        for (var i = 0; i < mDifficulty; i++) {
            mPictures[i].setBorder(hasBorder)
        }
    }

    /**
     * 游戏开始。
     */
    this.start = function() {
        // 重新创建所有图片
        if (mStatus != GameStatus.END) {
            createPictures()
        }

        // 重置成员变量
        const baseCount = Math.sqrt(mDifficulty)
        mEmptyPoint = new Point(Const.EMPTY_AREA_X + mPictures[0].getSize() * baseCount,
            Const.EMPTY_AREA_Y + mPictures[0].getSize() * (baseCount - 1))
        mStatus = GameStatus.PLAYING

        // 右下角图片控件右移
        const oldPosition = mPictures[mDifficulty - 1].getPosition()
        const position = new Point(oldPosition.x, oldPosition.y)
        mPictures[mDifficulty - 1].setPosition(mEmptyPoint)
        mEmptyPoint = position

        // 乱序其它图片控件的位置
        var index1, index2
        for (var i = 0; i < mDifficulty - 1; i++) {
            do {
                index1 = Math.floor(Math.random() * 100) % (mDifficulty - 1)
                index2 = Math.floor(Math.random() * 100) % (mDifficulty - 1)
            } while (index1 == index2)
            swapPictures(index1, index2)
        }
    }

    /**
     * 交换两个图片控件的位置。
     * @param index1    图片控件1的索引
     * @param index2    图片控件2的索引
     */
    function swapPictures(index1, index2) {
        var temp = mPictures[index1].getPosition()
        const position1 = new Point(temp.x, temp.y)
        temp = mPictures[index2].getPosition()
        const position2 = new Point(temp.x, temp.y)
        mPictures[index1].setPosition(position2)
        mPictures[index2].setPosition(position1)
    }

    // 设置图片
    this.setPicture(Const.PICTURE_FILE_PATH)
}
